//
//  AppNetwork.m
//  XiaoMiao
//
//  Created by 23 on 2017/11/5.
//  Copyright © 2017年 smallyou. All rights reserved.
//

#import "AppNetwork.h"
#import "AFNetworkActivityIndicatorManager.h"
#import "AFNetworking.h"
#import "AFHTTPSessionManager.h"
#import <CommonCrypto/CommonDigest.h>

/**
 HTTP请求方式
 */
typedef NS_ENUM(NSUInteger, HttpMethodEnum) {
    HttpMethodGetEnum = 1,                                          // get请求方法
    HttpMethodPostEnum,                                             // post请求方法
};


/**
 基础属性
 */
static NSString *app_privateNetworkBaseUrl = nil;                   // 根 URL
static BOOL app_isEnableInterfaceDebug = NO;                        // 是否是 debug 状态
static NSDictionary *app_httpHeaders = nil;                         // 请求头
static AppResponseType app_responseType = AppResponseTypeJSON;      // 响应格式，默认为JSON
static AppRequestType  app_requestType  = AppRequestTypeJSON;       // 请求格式，默认为JSON
static NSMutableArray *app_requestTaskArrayM;                       // 请求任务数组
static BOOL app_isCallbackOnCancelRequest = YES;                    // 取消请求时，是否要回调
static NSTimeInterval app_timeout = 30.0f;                          // 请求超时时间


@implementation AppNetwork

#pragma mark - 基础属性配置
/**更新基础URL*/
+ (void)updateBaseUrl:(NSString *)baseUrl {
    app_privateNetworkBaseUrl = baseUrl;
}

/**获取基础URL*/
+ (NSString *)baseUrl {
    return app_privateNetworkBaseUrl;
}

/**设置超时时间*/
+ (void)setTimeout:(NSTimeInterval)timeout {
    app_timeout = timeout;
}

/**开启调试*/
+ (void)enableInterfaceDebug:(BOOL)isDebug {
    app_isEnableInterfaceDebug = isDebug;
}

/**是否开启调试*/
+ (BOOL)isDebug {
    return app_isEnableInterfaceDebug;
}

/**配置请求格式，默认为JSON。如果要求传XML或者PLIST，请在全局配置一下*/
+ (void)configRequestType:(AppRequestType)requestType responseType:(AppResponseType)responseType  callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest {
    app_requestType = requestType;
    app_responseType = responseType;
    app_isCallbackOnCancelRequest = shouldCallbackOnCancelRequest;
}

/**配置公共请求头*/
+ (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders {
    app_httpHeaders = httpHeaders;
}

/**当前所有的网络请求任务*/
+ (NSMutableArray *)allTasks {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (app_requestTaskArrayM == nil) {
            app_requestTaskArrayM = [[NSMutableArray alloc] init];
        }
    });
    
    return app_requestTaskArrayM;
}


#pragma mark - 公共API
/**取消所有的网络请求*/
+ (void)cancelAllRequest {
    @synchronized(self) {
        [[self allTasks] enumerateObjectsUsingBlock:^(AppURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([task isKindOfClass:[AppURLSessionTask class]]) {
                [task cancel];
            }
        }];
        
        [[self allTasks] removeAllObjects];
    };
}

/**取消指定URL的请求*/
+ (void)cancelRequestWithURL:(NSString *)url {
    if (url == nil) {
        return;
    }
    
    @synchronized(self) {
        [[self allTasks] enumerateObjectsUsingBlock:^(AppURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([task isKindOfClass:[AppURLSessionTask class]]
                && [task.currentRequest.URL.absoluteString hasSuffix:url]) {
                [task cancel];
                [[self allTasks] removeObject:task];
                return;
            }
        }];
    };
}



#pragma mark - GET请求接口
/**GET请求*/
+ (AppURLSessionTask *)getWithUrl:(NSString *)url refreshCache:(BOOL)refreshCache params:(NSDictionary *)params success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    
    return [self _requestWithUrl:url refreshCache:refreshCache httpMedth:HttpMethodGetEnum params:params progress:nil success:success fail:fail];
}


#pragma mark - POST请求接口
/**POST请求 - 是否缓存，默认不缓存*/
+ (AppURLSessionTask *)postWithUrl:(NSString *)url refreshCache:(BOOL)refreshCache params:(NSDictionary *)params success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    
    return [self _requestWithUrl:url refreshCache:refreshCache httpMedth:HttpMethodPostEnum params:params progress:nil success:success fail:fail];
}

/**POST请求 - 不带缓存*/
+ (AppURLSessionTask *)postWithUrl:(NSString *)url params:(NSDictionary *)params success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    return [self _requestWithUrl:url refreshCache:NO httpMedth:HttpMethodPostEnum params:params progress:nil success:success fail:fail];
}


#pragma mark - POST/GET请求方法的具体实现
+ (AppURLSessionTask *)_requestWithUrl:(NSString *)url refreshCache:(BOOL)refreshCache httpMedth:( HttpMethodEnum)httpMethod params:(NSDictionary *)params progress:(AppNetProgress)progress success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    
    // 根据传进的url得出请求的完整路径
    NSString *absolute = [self absoluteUrlWithPath:url];

    //判断URL路径
    if ([NSURL URLWithString:absolute] == nil) {
        NSLog(@"\n------ URLString无效，无法生成URL，可能是url中有中文 ------\n");
        return nil;
    }
    
    //重新处理请求参数
    NSMutableDictionary *paramsM = [NSMutableDictionary dictionaryWithDictionary:params];
    
    //获取请求管理者
    AFHTTPSessionManager *manager = [self manager:url];
    
    //定义请求任务
    AppURLSessionTask *sessionTask = nil;
    
    //判断请求类型
    switch (httpMethod) {
            case HttpMethodGetEnum: { // GET 请求方法
                sessionTask = [manager GET:absolute parameters:paramsM progress:^(NSProgress * _Nonnull downloadProgress) {
                    if (progress) {
                        progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
                    }
                    
                } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                    if ([responseObject isKindOfClass:[NSDictionary class]]) {
                        //全局判断错误
                        if (![responseObject[@"statusCode"] isEqualToString:@"200"]) {
                            AppError *error = [AppError errorWithCode:[responseObject[@"statusCode"] integerValue] errMessage:responseObject[@"msg"]];
                            
                            error.response = [NSDictionary dictionaryWithDictionary:responseObject];
                            
                            if (error.code == 84512) {
                                //登录超时，请重新登录(或者未登录)
                                [AppNotificationHelpper postNotificationName:kLoginSessionTimeoutNotification object:nil];
                            }
                            
                            [self handleCallbackWithError:error fail:fail];
                            
                            [[self allTasks] removeObject:task];
                            
                            if ([self isDebug]) {
                                [self logWithFailError:error url:absolute params:paramsM];
                            }
                            return;
                        }
                    }
                    
                    [self successResponse:responseObject callback:success];
                    
                    [[self allTasks] removeObject:task];
                    
                    if ([self isDebug]) {
                        [self logWithSuccessResponse:responseObject url:absolute params:paramsM];
                    }
                    
                } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                    [self handleCallbackWithError:error fail:fail];
                    
                    [[self allTasks] removeObject:task];
                    
                    if ([self isDebug]) {
                        [self logWithFailError:error url:absolute params:paramsM];
                    }
                }];
            }
            break;
            
            case HttpMethodPostEnum: { // POST 请求方法
                if (refreshCache) {
                    // 如果获取磁盘的缓存
                    id response = [self cahceResponseWithURL:absolute parameters:paramsM];
                    
                    [self successResponse:response callback:success];
                    
                    if ([self isDebug]) { // 打印 log
                        NSLog(@"-------- 获取到磁盘缓存数据 ----------");
                        // [self logWithSuccessResponse:response url:absolute params:params];
                    }
                    return nil;
                }
                
                //发起请求
                sessionTask = [manager POST:absolute parameters:paramsM progress:^(NSProgress * _Nonnull downloadProgress) {
                    if (progress) {
                        progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
                    }
                    
                } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                    if ([responseObject isKindOfClass:[NSData class]]) {
                        //如果接口返回的是二进制数据
                        NSLog(@">>>>>>>>>>>>>>.smallyou:接口返回的是二进制数据");
                        return;
                    }
                    
                    //如果接口返回的是json字典
                    if ([responseObject isKindOfClass:[NSDictionary class]]) {
                        //全局判断错误
                        if (![responseObject[@"statusCode"] isEqualToString:@"200"]) {
                            
                            AppError *error = [AppError errorWithCode:[responseObject[@"statusCode"] integerValue] errMessage:responseObject[@"msg"]];
        
                            if (error.code == 84512) {
                                //登录超时，请重新登录(或者未登录)
                                [AppNotificationHelpper postNotificationName:kLoginSessionTimeoutNotification object:nil];
                            }
                            
                            [self handleCallbackWithError:error fail:fail];
                            
                            [[self allTasks] removeObject:task];
                            
                            if ([self isDebug]) {
                                [self logWithFailError:error url:absolute params:paramsM];
                            }
                            return;
                        }
                    }
                    
                    //响应成功
                    [self successResponse:responseObject callback:success];
                    
                    //缓存磁盘
                    if (refreshCache) { // 缓存请求到的数据
                        NSLog(@"-------- 缓存数据到磁盘 ----------");
                        [self cacheResponseObject:responseObject request:task.currentRequest parameters:paramsM];
                    }
                    
                    // 移除当前请求任务
                    [[self allTasks] removeObject:task];
                    
                    if ([self isDebug]) { // 打印 log
                        [self logWithSuccessResponse:responseObject url:absolute params:paramsM];
                    }
                    
                } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                    [self handleCallbackWithError:error fail:fail];
                    
                    [[self allTasks] removeObject:task];
                    
                    if ([self isDebug]) {
                        [self logWithFailError:error url:absolute params:paramsM];
                    }
                }];
            }
            break;
    }
    
    if (sessionTask) { // 添加请求任务到数组中
        [[self allTasks] addObject:sessionTask];
    }
    
    return sessionTask;
}


#pragma mark - 私有方法
/**
 根据传进的url得出请求的完整路径
 
 @param path 可以是带 http 请求头的全路径, 也可以的目录路径
 
 @return 完整全路径
 */
+ (NSString *)absoluteUrlWithPath:(NSString *)path {
    if (path == nil || path.length == 0) {
        return @"";
    }
    
    // 1.如果之前没配置基础路径baseUrl
    if ([self baseUrl] == nil || [[self baseUrl] length] == 0) {
        if (![path hasPrefix:@"http://"] && ![path hasPrefix:@"https://"]) {
            NSLog(@"\n------- 请求的路径不完整 -------\n");
        }
        return path;
    }
    
    // 2.之前配置了基础路径baseUrl
    NSString *absoluteUrl = path;
    
    if ([absoluteUrl hasPrefix:@"http://"] || [absoluteUrl hasPrefix:@"https://"]) {
        return absoluteUrl;
    }
    
    // 2.1对拼接进行处理
    if ([[self baseUrl] hasSuffix:@"/"]) { // baseUrl以'/'结束
        if ([path hasPrefix:@"/"]) {
            NSMutableString * mutablePath = [NSMutableString stringWithString:path];
            [mutablePath deleteCharactersInRange:NSMakeRange(0, 1)];
            absoluteUrl = [NSString stringWithFormat:@"%@%@", [self baseUrl], mutablePath];
        }else {
            absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
        }
        
    }else {
        if ([path hasPrefix:@"/"]) {
            absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
        }else {
            absoluteUrl = [NSString stringWithFormat:@"%@/%@", [self baseUrl], path];
        }
    }
    
    return absoluteUrl;
}


/**
 获取请求管理者

 @param url URL路径
 @return 管理器
 */
+ (AFHTTPSessionManager *)manager:(NSString *)url
{
    // 开启转圈圈
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    //创建管理器
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    //设置请求类型
    switch (app_requestType) {
            case AppRequestTypeJSON:
            manager.requestSerializer = [AFJSONRequestSerializer serializer];
            break;
            
            case AppRequestTypePlainText:
            manager.requestSerializer = [AFHTTPRequestSerializer serializer];
            break;
    }
    
    //设置响应类型
    switch (app_responseType) {
            case AppResponseTypeJSON: {
                AFJSONResponseSerializer *response = [AFJSONResponseSerializer serializer];
                manager.responseSerializer = response;
            }
            break;
            
            case AppResponseTypeXML:
            manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
            break;
            
            case AppResponseTypeData:
            manager.responseSerializer = [AFHTTPResponseSerializer serializer];
            break;
    }
    
    //设置请求的编码方式为UTF-8
    manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
    
    //设置当前APP特有的请求头
    if ([AppGlobal isLogin]) {
        NSString *sessionId = [AppGlobal getSid];
        if (sessionId.length) {
            NSString *cookie = [NSString stringWithFormat:@"JSESSIONID=%@", sessionId];
            [self configCommonHttpHeaders:@{@"Cookie": cookie}];
        }
    }
    
    //设置公共请求头
    for (NSString *key in app_httpHeaders.allKeys) {
        if (app_httpHeaders[key] != nil) {
            [manager.requestSerializer setValue:app_httpHeaders[key] forHTTPHeaderField:key];
        }
    }
    
    //设置可处理的响应格式
    NSArray *serializerArray = @[@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/*"];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:serializerArray];
    
    //设置请求超时请求时间
    manager.requestSerializer.timeoutInterval = app_timeout;
    
    //设置请求并发最大数
    manager.operationQueue.maxConcurrentOperationCount = 6;
    
    return manager;
}



/**
 解析成功的响应请求

 @param responseData 响应数据
 @param success 成功后的回调
 */
+ (void)successResponse:(id)responseData callback:(AppResponseSuccess)success {
    if (success) {
        success([self tryToParseData:responseData]);
    }
}


/**
 取消请求的回调设置
*/
+ (void)handleCallbackWithError:(NSError *)error fail:(AppResponseFail)fail {
    
    //兼容系统错误
    AppError *appError;
    if ([error isKindOfClass:[AppError class]]) {
        appError = (AppError * )error;
    }else{
        appError = [AppError errorWithNSError:error];

    }
    
    //如果是取消网络请求
    if ([error code] == NSURLErrorCancelled) {
        if (app_isCallbackOnCancelRequest) {
            if (fail) {
                fail(appError);
            }
        }
        
    }else {
        if (fail) {
            fail(appError);
        }
    }
}


/**
 解析服务器返回的数据
 如果responseData是二进制, 就返回解析后的数据, 如果解析错误, 就直接返回二进制
 
 @param responseData 返回的数据

 @return 解析后的数据
 */
+ (id)tryToParseData:(id)responseData {
    if (![responseData isKindOfClass:[NSData class]]
        || responseData == nil) { // 如果返回的数据不是二进制或 nil,直接返回
        return responseData;
    }
    
    // 尝试解析成JSON
    NSError *error = nil;
    NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error];
    
    if (error != nil) { // 解析错误, 直接返回
        return responseData;
    }else {
        return response;
    }
}


/**
 打印成功后的日志

 @param response 响应内容
 @param url url
 @param params 参数
 */
+ (void)logWithSuccessResponse:(id)response url:(NSString *)url params:(NSDictionary *)params {
    
    NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n", url, [self jsonStringFromDict:params], [self jsonStringFromDict:response]);
}


/**
 打印失败的日志

 @param error 错误
 @param url URL
 @param params 参数
 */
+ (void)logWithFailError:(NSError *)error url:(NSString *)url params:(id)params {
    if ([error code] == NSURLErrorCancelled) {
        NSLog(@"\nRequest was canceled, URL: %@\n params: %@\n", url, [self jsonStringFromDict:params]);
    }else {
        NSLog(@"\nRequest fail, URL: %@\n params: %@\n error: %@\n", url, [self jsonStringFromDict:params], [error localizedDescription]);
    }
}

/**
 字典转 json
 @param dict  字典
 @return json
 */
+ (NSString *)jsonStringFromDict:(NSDictionary *)dict {
    if (![dict isKindOfClass:[NSDictionary class]] || dict == nil) {
        return @"";
    }
    
    NSError *error;
    NSData* infoJsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
    if (error) {
        NSLog(@"\n转字典error: %@", error);
        return @"";
    }
    
    NSString *json = [[NSString alloc] initWithData:infoJsonData encoding:NSUTF8StringEncoding];
    return json;
}


#pragma mark - 缓存相关
/**
 根据路径和参数获取缓存数据
 
 @return 网络缓存数据
 */
+ (id)cahceResponseWithURL:(NSString *)url parameters:params {
    id cacheData = nil;
    
    if (url) {
        // 尝试从磁盘获取缓存数据
        NSString *directoryPath = cachePath();
        NSString *absoluteURL = [self generateGETAbsoluteURL:url params:params];
        NSString *key = [NSString app_md5:absoluteURL];
        NSString *path = [directoryPath stringByAppendingPathComponent:key];
        
        NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
        if (data) {
            cacheData = data;
            NSLog(@"根据 URL 从磁盘中读取缓存数据: %@\n", url);
        }
    }
    
    return cacheData;
}

/**
 将获取到的数据缓存到磁盘中
 */
+ (void)cacheResponseObject:(id)responseObject request:(NSURLRequest *)request parameters:params {
    if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {
        NSString *directoryPath = cachePath();
        
        NSError *error = nil;
        
        if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
            [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
            if (error) {
                NSLog(@"创建磁盘文件错误: %@\n", error);
                return;
            }
        }
        
        NSString *absoluteURL = [self generateGETAbsoluteURL:request.URL.absoluteString params:params];
        NSString *key = [NSString app_md5:absoluteURL];
        NSString *path = [directoryPath stringByAppendingPathComponent:key];
        NSDictionary *dict = (NSDictionary *)responseObject;
        
        NSData *data = nil;
        if ([dict isKindOfClass:[NSData class]]) {
            data = responseObject;
        } else {
            data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
        }
        
        if (data && error == nil) {
            BOOL isOk = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
            if (isOk) {
                NSLog(@"cache file ok for request: %@\n", absoluteURL);
            } else {
                NSLog(@"cache file error for request: %@\n", absoluteURL);
            }
        }
    }
}



// 仅对一级字典结构起作用
+ (NSString *)generateGETAbsoluteURL:(NSString *)url params:(id)params {
    if (params == nil || ![params isKindOfClass:[NSDictionary class]] || [params count] == 0) {
        return url;
    }
    
    NSString *queries = @"";
    for (NSString *key in params) {
        id value = [params objectForKey:key];
        
        if ([value isKindOfClass:[NSDictionary class]]) {
            continue;
        } else if ([value isKindOfClass:[NSArray class]]) {
            continue;
        } else if ([value isKindOfClass:[NSSet class]]) {
            continue;
        } else {
            queries = [NSString stringWithFormat:@"%@%@=%@&", (queries.length == 0 ? @"&" : queries), key, value];
        }
    }
    
    if (queries.length > 1) {
        queries = [queries substringToIndex:queries.length - 1];
    }
    
    if (([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) && queries.length > 1) {
        
        if ([url rangeOfString:@"?"].location != NSNotFound || [url rangeOfString:@"#"].location != NSNotFound) {
            url = [NSString stringWithFormat:@"%@%@", url, queries];
            
        } else {
            queries = [queries substringFromIndex:1];
            url = [NSString stringWithFormat:@"%@?%@", url, queries];
        }
    }
    
    return url.length == 0 ? queries : url;
}



/** 缓存路径 */
static inline NSString *cachePath() {
    NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
    return  [path stringByAppendingPathComponent: @"KLNetworkingCaches"];
}


#pragma mark - 下载文件

/**
 下载文件

 @param url 服务器地址
 @param saveToPath 存储文件的地址
 @param progressBlock 下载进度
 @param success 成功回调
 @param failure 失败回调
 @return 下载任务
 */
+ (AppURLSessionTask *)downloadWithUrl:(NSString *)url saveToPath:(NSString *)saveToPath progress:(AppNetProgress)progressBlock success:(AppResponseSuccess)success failure:(AppResponseFail)failure {
    
    // 根据传进的url得出请求的完整路径
    NSString *absolute = [self absoluteUrlWithPath:url];
    
    // 判断url是否正确
    if ([NSURL URLWithString:absolute] == nil) {
        NSLog(@"absolute: %@", absolute);
        NSLog(@"\n------ URLString无效，无法生成URL，可能是url中有中文 ------\n");
        return nil;
    }
    
    // 根据下载的服务器地址创建NSURLRequest
    NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:absolute]];
    
    // 创建请求管理器
    AFHTTPSessionManager *manager = [self manager:url];
    
    // 发起下载任务
    AppURLSessionTask *sessionTask = [manager downloadTaskWithRequest:downloadRequest progress:^(NSProgress * _Nonnull downloadProgress) {
        if (progressBlock) {
            progressBlock(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
        }
    } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        return [NSURL fileURLWithPath:saveToPath];
        
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        [[self allTasks] removeObject:sessionTask];
        
        if (error == nil) {
            if (success) {
                success(filePath.absoluteString);
            }
            
            if ([self isDebug]) {
                NSLog(@"Download success for url: %@", [self absoluteUrlWithPath:url]);
            }
        }
        else {
            [self handleCallbackWithError:error fail:failure];
            
            if ([self isDebug]) {
                NSLog(@"Download fail for url: %@, reason : %@", [self absoluteUrlWithPath:url], [error description]);
            }
        }
    }];
    
    [sessionTask resume];
    
    if (sessionTask) {
        [[self allTasks] addObject:sessionTask];
    }
    
    return sessionTask;
}

#pragma mark - 文件上传

/**
 文件上传接口

 @param url 服务器地址
 @param uploadingFile 上传的文件路径
 @param progress 上传的进度
 @param success 成功回调
 @param fail 失败回调
 @return 上传任务
 */
+ (AppURLSessionTask *)uploadFileWithUrl:(NSString *)url uploadingFile:(NSString *)uploadingFile progress:(AppNetProgress)progress success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    
    NSLog(@"上传文件的路径: %@", uploadingFile);
    
    // 根据传进的url得出请求的完整路径
    NSString *absolute = [self absoluteUrlWithPath:url];
    
    // 判断url是否正确
    if ([NSURL URLWithString:absolute] == nil) {
        NSLog(@"absolute: %@", absolute);
        NSLog(@"\n------ URLString无效，无法生成URL，可能是url中有中文 ------\n");
        return nil;
    }
    
    //创建请求管理器
    AFHTTPSessionManager *manager = [self manager:url];
    
    //发起POST请求
    AppURLSessionTask *sessionTask = [manager POST:absolute parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        
        NSData *fileData = [NSData dataWithContentsOfFile:uploadingFile];
        
        NSLog(@"上传文件大小: %.2fKb", [fileData length] * 1.0 / 1024.0);
        
        // 上传文件(目前只是上传语音, 以后上传其他再修改)，以文件流的格式 $$$$$ name、fileName、mimeType参数目前先固定
        [formData appendPartWithFileData:fileData name:@"data_stream[]" fileName:@"hhhhh.amr" mimeType:@"amr"];
    }progress:^(NSProgress * _Nonnull uploadProgress) {
        if (progress) {
            progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
        }
        
        CGFloat precent = uploadProgress.completedUnitCount * 1.0 / uploadProgress.totalUnitCount;
        NSLog(@"文件总大小: %.2fKb, 上传百分比: %.2f", uploadProgress.totalUnitCount * 1.0 / 1024.0, precent);
        
    }success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [self successResponse:responseObject callback:success];
        
        [[self allTasks] removeObject:task];
        
        if ([self isDebug]) {
            [self logWithSuccessResponse:responseObject url:absolute params:nil];
        }
        
    }failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [self handleCallbackWithError:error fail:fail];
        
        [[self allTasks] removeObject:task];
        
        if ([self isDebug]) {
            [self logWithFailError:error url:absolute params:nil];
        }
    }];
    
    [sessionTask resume];
    
    if (sessionTask) {
        [[self allTasks] addObject:sessionTask];
    }
    
    return sessionTask;
}

#pragma mark - 单张图片上传
/**
 单张图片上传

 @param image 图片
 @param url 上传服务器路径
 @param parameters 上传的参数
 @param progress 上传的进度
 @param success 成功的回调
 @param fail 失败的回调
 @return 上传任务
 */
+ (AppURLSessionTask *)uploadWithImage:(UIImage *)image url:(NSString *)url parameters:(NSDictionary *)parameters progress:(AppNetProgress)progress success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    return [self uploadWithImage:image url:url filename:@"hhhhh.png" name:@"picturePath" mimeType:@"image/png" parameters:parameters progress:progress success:success fail:fail];
}


/**
 上传图片

 @param image 图片对象
 @param url 上传的服务器路径
 @param filename 文件名称
 @param name 图片名称
 @param mimeType 媒体的类型
 @param parameters 参数
 @param progress 上传机大怒
 @param success 成功回到
 @param fail 失败回调
 @return 上传任务
 */
+ (AppURLSessionTask *)uploadWithImage:(UIImage *)image url:(NSString *)url filename:(NSString *)filename name:(NSString *)name mimeType:(NSString *)mimeType parameters:(NSDictionary *)parameters progress:(AppNetProgress)progress success:(AppResponseSuccess)success fail:(AppResponseFail)fail {
    
    // 根据传进的url得出请求的完整路径
    NSString *absolute = [self absoluteUrlWithPath:url];
    
    if ([NSURL URLWithString:absolute] == nil) {
        NSLog(@"absolute: %@", absolute);
        NSLog(@"\n------ URLString无效，无法生成URL，可能是url中有中文 ------\n");
        return nil;
    }
    
    AFHTTPSessionManager *manager = [self manager:url];
    
    
    AppURLSessionTask *sessionTask = [manager POST:absolute parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        
        NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
        
        NSLog(@"上传图片大小: %.2fKb", [imageData length] * 1.0 / 1024.0);
        
        NSString *imageFileName = filename;
        if (filename == nil || ![filename isKindOfClass:[NSString class]] || filename.length == 0) {
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            formatter.dateFormat = @"yyyyMMddHHmmss";
            NSString *str = [formatter stringFromDate:[NSDate date]];
            imageFileName = [NSString stringWithFormat:@"%@.jpg", str];
        }
        
        // 上传图片，以文件流的格式
        [formData appendPartWithFileData:imageData name:name fileName:imageFileName mimeType:mimeType];
    }progress:^(NSProgress * _Nonnull uploadProgress) {
        if (progress) {
            progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
        }
        
    }success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [self successResponse:responseObject callback:success];
        
        [[self allTasks] removeObject:task];
        
        if ([self isDebug]) {
            [self logWithSuccessResponse:responseObject url:absolute params:parameters];
        }
        
    }failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [self handleCallbackWithError:error fail:fail];
        
        [[self allTasks] removeObject:task];
        
        if ([self isDebug]) {
            [self logWithFailError:error url:absolute params:nil];
        }
    }];
    
    [sessionTask resume];
    
    if (sessionTask) {
        [[self allTasks] addObject:sessionTask];
    }
    
    return sessionTask;
}

#pragma mark - 多张图片上传

/**
 上传多想图片

 @param imageArray 图片对象数组
 @param url 上传的服务器url
 @param params 参数
 @param complete 回调
 */
+ (void)uploadWithImageArray:(NSArray <UIImage *>*)imageArray url:(NSString *)url params:(NSDictionary *)params complete:(void(^)(NSMutableArray <id>*successResponses, NSMutableArray <UIImage *>*successImages))complete {
    
    NSMutableArray *successResponses = [NSMutableArray array];
    NSMutableArray *successImages = [NSMutableArray array];
    
    // 保存上传任务数组
    // NSMutableArray *taskArrayM = [NSMutableArray array];
    
    dispatch_group_t group = dispatch_group_create();
    for (NSInteger i = 0; i < imageArray.count; i++) {
        dispatch_group_enter(group);
        
        AppURLSessionTask *sessionTask = [self uploadWithImage:imageArray[i] url:url filename:@"hhhhh.png" name:@"PicturePath" mimeType:@"image/png" parameters:params progress:^(int64_t bytesWritten, int64_t totalBytesWritten) {
            // NSLog(@"第 %d 张图片上传进度: %f", (int)i + 1, (bytesWritten * 1.0 / totalBytesWritten));
        } success:^(id response) {
            NSLog(@"第 %d 张图片上传成功: %@", (int)i + 1, response);
            @synchronized (successResponses) { // NSMutableArray 是线程不安全的，所以加个同步锁
                [successResponses addObject:response];
            }
            @synchronized (successImages) {
                [successImages addObject:imageArray[i]];
            }
            dispatch_group_leave(group);
            
        } fail:^(NSError *error) {
            NSLog(@"第 %d 张图片上传失败: %@", (int)i + 1, error);
            dispatch_group_leave(group);
        }];
        
        [sessionTask resume];
        
        // [taskArrayM addObject:sessionTask];
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"------ 上传完成! ----------");
        complete(successResponses, successImages);
    });
}

@end
